#include "environment.h"

void *environment_thread(void *argv) {
    int32_t i, ret;
    uint8_t ack[ENVIRONMENT_ACK_MAX], cmd[3] = {0xa5, 0x83, 0x28};
    Environment::ENVIRONMENTPARAM *param = (Environment::ENVIRONMENTPARAM *)argv;
    // 环境信号
    write(*param->fd, cmd, sizeof(cmd));
    // 循环读取环境数据
    while (*param->signal) {
        // 重置数据
        ret = -1;
        memset(ack, 0, sizeof(ack));
        // 等待数据
        sleep(1);
        // 获取数据
        if ((ret = read(*param->fd, ack, sizeof(ack))) != 9 && ret != 15 && ret != 24) {
            printf("environment acquisition failed! ret:%d\n", ret);
            continue;
        }
        // 保存数据
        for (i = 0; i < ENVIRONMENT_ACK_MAX; i++)
            (*param->ack)[i] = ack[i];
        // 光照
        if (ack[0] == 0x5a && ack[1] == 0x5a && ack[2] == 0x15) {
            // 保存光照
            *param->lightIntensity = ack[4] << 24 | ack[5] << 16 | ack[6] << 8 | ack[7];
        }
        // 温度、气压、湿度、海拔
        if (ack[9] == 0x5a && ack[10] == 0x5a && ack[11] == 0x45) {
            // 保存温度
            *param->temperature = ack[13] << 8 | ack[14];
            // 保存气压
            *param->pressure = ack[15] << 24 | ack[16] << 16 | ack[17] << 8 | ack[18];
            // 保存湿度
            *param->humidity = ack[19] << 8 | ack[20];
            // 保存海拔
            *param->altitude = ack[21] << 8 | ack[22];
        }
    }
    // 退出线程
    pthread_exit(param->tid);
    return NULL;
}

Environment::Environment(const string path, const uint32_t baudrate) {
    struct termios termios;
    // 初始化数据
    this->fd = -1, this->tid = -1, this->signal = 0, this->pressure = 0, this->humidity = 0, this->altitude = 0, this->temperature = 0, this->lightIntensity = 0;
    vector< uint8_t >().swap(this->ack);
    memset(&this->param, 0, sizeof(this->param));
    // 打开环境传感器
    if ((this->fd = open(path.c_str(), O_RDWR)) == -1)
        throw runtime_error("open '" + path + "' error!\n");
    // 清空数据
    memset(&termios, 0, sizeof(termios));
    // 设置数据
    termios.c_cflag |= (CLOCAL | CREAD);     // O_RDWR
    termios.c_cflag &= ~CSIZE;               // 清空数据位
    termios.c_cflag &= ~ENVIRONMENT_CRTSCTS; // 无硬件控制
    termios.c_cflag |= CS8;                  // 数据位：8
    termios.c_cflag &= ~CSTOPB;              // 停止位：1
    termios.c_cflag &= ~PARENB;              // 无校验模式
    // 设置波特率
    switch (baudrate) {
    case 9600:
        cfsetospeed(&termios, B9600);
        cfsetispeed(&termios, B9600);
        break;
    case 115200:
        cfsetospeed(&termios, B115200);
        cfsetispeed(&termios, B115200);
        break;
    case 19200:
        cfsetospeed(&termios, B19200);
        cfsetispeed(&termios, B19200);
        break;
    }
    // 刷新输出队列
    tcflush(this->fd, TCIFLUSH);
    // 改变配置
    tcsetattr(this->fd, TCSANOW, &termios);
    // 设置数据最大长度
    this->ack.resize(ENVIRONMENT_ACK_MAX);
    //  创建线程
    this->signal = 1;
    this->param.fd = &this->fd;
    this->param.tid = &this->tid;
    this->param.signal = &this->signal;
    this->param.pressure = &this->pressure;
    this->param.humidity = &this->humidity;
    this->param.altitude = &this->altitude;
    this->param.temperature = &this->temperature;
    this->param.lightIntensity = &this->lightIntensity;
    this->param.ack = &this->ack;
    if (pthread_create(&this->tid, NULL, environment_thread, &this->param)) {
        close(this->fd);
        throw runtime_error("environment pthread_create error!\n");
    }
}

Environment::~Environment() {
    // 关闭线程
    if (this->signal != 0)
        this->signal = 0;
    // 关闭文件
    if (this->fd != -1)
        close(this->fd);
}

vector< uint8_t > &Environment::get_ack() {
    return this->ack;
}

uint32_t &Environment::get_pressure() {
    return this->pressure;
}

uint32_t &Environment::get_humidity() {
    return this->humidity;
}

uint32_t &Environment::get_altitude() {
    return this->altitude;
}

uint32_t &Environment::get_temperature() {
    return this->temperature;
}

uint32_t &Environment::get_lightIntensity() {
    return this->lightIntensity;
}